# -*- coding: utf-8 -*-
import json
import os

import importlib
from celery import Celery
from flask import Flask
from flask import make_response
from flask_babel import Babel
from flask_restful import Api
from flask_redis import FlaskRedis
from flask_pymongo import PyMongo
from flask_collect import Collect
from flask_mail import Mail
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from tank.core import discover
from tank.core.documents import json_convert

app = Flask(__name__)
db = SQLAlchemy()
manager = Manager(app)
collect = Collect()
mongo = PyMongo()
redis = FlaskRedis()
babel = Babel()
api = Api(app, prefix='/api')


@app.before_first_request
def initialize_app_on_first_request():
    pass


def create_app(extra_config_settings={}):
    """
    Initialize Flask applicaton
    """
    settings = importlib.import_module(os.environ.get("TANK_SETTINGS", 'tank.settings'))
    app.config.from_object(settings)
    app.config.update(extra_config_settings)
    if app.testing:
        app.config['WTF_CSRF_ENABLED'] = False

    app.static_folder = os.path.join(app.config['APP_ROOT'], 'static')
    app.template_folder = os.path.join(app.config['APP_ROOT'], 'templates')

    # Babel
    babel.init_app(app)

    db.init_app(app)
    # Collect
    collect.init_app(app)
    collect.init_script(manager)

    # SqlAlchemy
    Migrate(app, db)
    manager.add_command('db', MigrateCommand)

    # Mongodb
    mongo.init_app(app)

    # Redis
    redis.init_app(app)

    # Setup Flask-Mail
    mail = Mail(app)

    # Setup WTForms CsrfProtect
    # CsrfProtect(app)

    # Define bootstrap_is_hidden_field for flask-bootstrap's bootstrap_wtf.html
    from wtforms.fields import HiddenField

    def is_hidden_field_filter(field):
        return isinstance(field, HiddenField)

    app.jinja_env.globals['bootstrap_is_hidden_field'] = is_hidden_field_filter

    init_email_error_handler(app)
    init_api_response(api)

    discover.load_applications(app)
    return app


def init_email_error_handler(app):
    """
    Initialize a logger to send emails on error-level messages.
    Unhandled exceptions will now send an email message to app.config.ADMINS.
    """
    if app.debug: return  # Do not send error emails while developing

    # Retrieve email settings from app.config
    host = app.config['MAIL_SERVER']
    port = app.config['MAIL_PORT']
    from_addr = app.config['MAIL_DEFAULT_SENDER']
    username = app.config['MAIL_USERNAME']
    password = app.config['MAIL_PASSWORD']
    secure = () if app.config.get('MAIL_USE_TLS') else None

    # Retrieve app settings from app.config
    to_addr_list = app.config['ADMINS']
    subject = app.config.get('APP_SYSTEM_ERROR_SUBJECT_LINE', 'System Error')

    # Setup an SMTP mail handler for error-level messages
    import logging
    from logging.handlers import SMTPHandler

    mail_handler = SMTPHandler(
        mailhost=(host, port),  # Mail host and port
        fromaddr=from_addr,  # From address
        toaddrs=to_addr_list,  # To address
        subject=subject,  # Subject line
        credentials=(username, password),  # Credentials
        secure=secure,
    )
    mail_handler.setLevel(logging.ERROR)
    app.logger.addHandler(mail_handler)

    # Log errors using: app.logger.error('Some error message')


def init_api_response(api):
    @api.representation('application/json')
    def output_json(data, code, headers=None):
        resp = make_response(json.dumps(json_convert({
            'group': 'user',
            'code': code,
            'success': code == 200,
            'data': data,
            'message': data if code != 200 else None
        })))
        resp.headers.extend(headers or {})
        return resp


def make_celery(app):
    celery = Celery(app.import_name, backend=app.config['CELERY_BACKEND'],
                    broker=app.config['CELERY_BROKER_URL'])
    celery.conf.update(app.config)
    TaskBase = celery.Task

    class ContextTask(TaskBase):
        abstract = True

        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)

    celery.Task = ContextTask
    return celery
